package ca.cbc.msb.gsa;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import ca.cbc.medialib.entity.Category;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import ca.cbc.medialib.entity.Media;
import ca.cbc.msb.entity.MSBMessage;
import ca.cbc.msb.exception.GSAFeedException;
import ca.cbc.msb.util.AppConfig;
import ca.cbc.msb.util.HttpServiceUtil;
import ca.cbc.msb.util.ProgressReport;

public class GSAService {
    private static Logger logger = LoggerFactory.getLogger(GSAService.class);
    private static Map<String,String> accountCategory = new HashMap<String,String>();
    private final static String DATA_SOURCE = AppConfig.getProperty("gsa.datasource");
    private final static String FEED_TYPE = AppConfig.getProperty("gsa.feedtype");
    private final static String GSA_URL = AppConfig.getProperty("gsa.url");
    private final static String FEED_URL = AppConfig.getProperty("feed.url");

    static {
        accountCategory.put("Digital Archives", "News");
        accountCategory.put("Documentaries", "Shows");
        accountCategory.put("Entertainment", "Shows");
        accountCategory.put("Kids", "Shows");
        accountCategory.put("News", "News");
        accountCategory.put("Radio", "Radio");
        accountCategory.put("Sports", "Sports");
        accountCategory.put("User Generated", "News");
        accountCategory.put("CBC Dev", "News");
    }

    public void deleteFromGSA(MSBMessage message) {
    	try {
			this.deleteMediaItem(message.getMedia());
			logger.info("Message ({}) delete from GSA successfully...", message);
			ProgressReport.instance.statusReport(message, "Delete from GSA Succeeded.");
		} catch (GSAFeedException e) {
            logger.error("!!!Error deleting from GSA.", e);
            logger.error("!!!MediaMessage ({}) delete from GSA failed...", message);
            ProgressReport.instance.statusReport(message, " Delete from GSA Failed.");
		}
    }
    
    public void submitToGSA(MSBMessage message) {
        try {
            if(!this.eligibleForIndexing(message.getMedia())){
                ProgressReport.instance.statusReport(message, " Was not eligible for GSA, ignored GSA submission");
                return;
            }
            
            if(message.getMedia().getApproved()) {
            	this.submitMediaItem(message);
            	logger.info("Message ({}) submission to GSA successfully...", message);
            } else { //if unapproved, delete from GSA
            	this.deleteMediaItem(message.getMedia());
            	logger.info("Message ({}) delete from GSA(unapproved) successfully...", message);
            }
            ProgressReport.instance.statusReport(message, " Submission to GSA Succeeded.");
        } catch (GSAFeedException e) {
            logger.error("!!!Error submitting to GSA.", e);
            logger.error("!!!MediaMessage ({}) submit to GSA failed...", message);
            ProgressReport.instance.statusReport(message, " Submission to GSA Failed.");
        }
    }
    
    public boolean eligibleForIndexing(Media media) {
        //if a media is dynamic, add it to GSA
        if (media.getCategories() == null || media.getCategories().size() == 0) {
            return true;
        }
        //Only index Medias in four base categories : Shows,News,Radio, Sports
        for (Category cat : media.getCategories()) {
            if (cat.getFullTitle().startsWith("Shows") ||
                    cat.getFullTitle().startsWith("Sports") ||
                    cat.getFullTitle().startsWith("News") ||
                    cat.getFullTitle().startsWith("Radio")) {
                return true;
            }
        }
        return false;
    }

    public void submitMediaItem(MSBMessage message) throws GSAFeedException {
        //build the XML feed
        String xmlStr;
        try{
        xmlStr = buildSubmitMediaXMLFeed(message);
        }catch(GSAFeedException e){
			logger.info("Release media without category, media id {}", message.getMedia().getReleaseId());
			return; 
		}
        try {
            xmlStr = URLEncoder.encode(xmlStr, "UTF-8");

            //build POST parameter
            String postParamStr = new StringBuilder().append("feedtype=" + FEED_TYPE)
                    .append("&datasource=" + DATA_SOURCE)
                    .append("&data=" + xmlStr).toString();

            //Post the XML feed to GSA site
            String responseStr = HttpServiceUtil.postText(GSA_URL, postParamStr);
            if (!responseStr.equalsIgnoreCase("Success"))
                throw new GSAFeedException("Error while submitting XML feed to GSA. Return text is not success.");
        } catch (UnsupportedEncodingException e) {
            throw new GSAFeedException("Error reading response", e);
        } catch (IOException e) {
            throw new GSAFeedException("Error reading response", e);
        }

    }

    public void deleteMediaItem(Media media) throws GSAFeedException {
        //build the XML feed
        String xmlStr = buildDeleteMediaXMLFeed(media);
        try {
            xmlStr = URLEncoder.encode(xmlStr, "UTF-8");

            //build POST parameter
            String postParamStr = new StringBuilder().append("feedtype=" + FEED_TYPE)
                    .append("&datasource=" + DATA_SOURCE)
                    .append("&data=" + xmlStr).toString();

            //Post the XML feed to GSA site
            String responseStr = HttpServiceUtil.postText(GSA_URL, postParamStr);
            if (!responseStr.equalsIgnoreCase("Success"))
                throw new GSAFeedException("Error while submitting XML feed to GSA. Return text is not success.");
        } catch (IOException e) {
            throw new GSAFeedException("Unsupported encoding", e);
        }
    }

    /**
     * This methods takes a URL and return the XML for deleting from GSA
     *
     * @param media - Media
     * @return XML text - String
     * @throws GSAFeedException 
     */
    protected String buildDeleteMediaXMLFeed(Media media) throws GSAFeedException {

        String displayURLStr = new StringBuilder().append(FEED_URL)
        .append(getGSAUrl(media))
        .append("/ID=" + media.getReleaseId()).toString();
        
        Document doc = DocumentHelper.createDocument();
        doc.addDocType("gsafeed", "-//Google//DTD GSA Feeds//EN", " ");
        Element gsafeed = doc.addElement("gsafeed");

        Element header = gsafeed.addElement("header");
        header.addElement("datasource").addText(DATA_SOURCE);
        header.addElement("feedtype").addText(FEED_TYPE);

        Element group = gsafeed.addElement("group");
        group.addElement("record")
                .addAttribute("action", "delete")
                .addAttribute("mimetype", "text/html")
                .addAttribute("displayurl", displayURLStr)
                .addAttribute("url", displayURLStr);

        return doc.asXML();
    }

    /**
     * This methods takes a message and build up XML feed for GSA submit media
     *
     * @param message - Message
     * @return XML text - String
     * @throws GSAFeedException 
     */
    protected String buildSubmitMediaXMLFeed(MSBMessage message) throws GSAFeedException {
        Media media = message.getMedia();
        String category = getGSAUrl(media);

        
        String displayURLStr = new StringBuilder().append(FEED_URL)
                .append(category)
                .append("/ID=" + media.getReleaseId()).toString();

        //creating gsafeed XML document
        String cdataStr = new StringBuilder().append("<html>\n")
                .append("<head>\n")
                .append("<META content=\"text/html; charset=UTF-8\" http-equiv=\"Content-Type\"/>\n")
                .append("<title>" + media.getTitle() + "</title>\n")
                .append("</head>\n")
                .append("<body>" + media.getDescription() + "</body>\n")
                .append("</html>\n").toString();


        Document doc = DocumentHelper.createDocument();
        doc.addDocType("gsafeed", "-//Google//DTD GSA Feeds//EN", " ");
        Element gsafeed = doc.addElement("gsafeed");

        Element header = gsafeed.addElement("header");
        header.addElement("datasource").addText(DATA_SOURCE);
        header.addElement("feedtype").addText(FEED_TYPE);

        Element group = gsafeed.addElement("group");
        Element record = group.addElement("record")
                .addAttribute("action", "add")
                .addAttribute("mimetype", "text/html")
                .addAttribute("displayurl", displayURLStr)
                .addAttribute("url", displayURLStr);

        Element metadata = record.addElement("metadata");

        //put the meta data map into XML and only add the field having values

        addProperty(metadata, media.getDescription(), "description");
        addProperty(metadata, media.getTitle(), "title");
        addProperty(metadata,media.getKeywords(),"keywords");
        addProperty(metadata,media.getThumbnailUrl(),"platformThumbnail");
        if(media.getCategories()!=null && media.getCategories().size() > 0 ){
            addProperty(metadata, media.getCategories().get(0).getFullTitle(),"category");
        }
        addProperty(metadata,media.getGenre(),"genre");
        addProperty(metadata,media.getAudioVideo(),"audioVideo");
        addProperty(metadata,media.getAvailableInMobile(),"availableInMobile");
        addProperty(metadata,media.getAccount(),"account");
        addProperty(metadata,media.getLiveOndemand(),"liveOnDemand");
        addProperty(metadata,media.getAdRules(),"adRules");
        addProperty(metadata,media.getSubtitle(),"subtitles");
        addProperty(metadata,media.getLength(),"runtime");
        addProperty(metadata,media.getAvailableInMobile(),"availableInMobile");

        //put CDATA into content
        record.addElement("content").addCDATA(cdataStr);

        //process air date, convert to different format
        Date airDate = media.getAirDate();
        if (airDate == null) {
            logger.info("Air date is empty!");
        } else {
                SimpleDateFormat platformDateParse = new SimpleDateFormat("E MMM d HH:mm:ss z yyyy");
                SimpleDateFormat RFC822Format = new SimpleDateFormat("E, d MMM yyyy HH:mm:ss z");

                //add air date UTC to meta data
                metadata.addElement("meta")
                        .addAttribute("name", "airdateUTC")
                        .addAttribute("content", platformDateParse.format(airDate));

                //add air date to meta data
                metadata.addElement("meta")
                        .addAttribute("name", "airdate")
                        .addAttribute("content", String.valueOf(airDate.getTime()));

                //add last-modified date to record
                record.addAttribute("last-modified", RFC822Format.format(airDate));
        } 
        return doc.asXML();
    }

    private String getGSAUrl(Media media) {
        String category;
        if(media.getStreamingCategoryFullTitle() == null){

            //make a category based on the account
            if(media.getAccount() ==null){
                category = "Shows";
            } else {
                category = accountCategory.get(media.getAccount());
            }
            if( category == null){
                category = "Shows";
            }
        } else {
            category = media.getStreamingCategoryFullTitle();
        }
        return category;
    }

    private void addProperty(Element metadata, String value, String key) {
        if (value != null && !value.equals(""))
            metadata.addElement("meta").addAttribute("name", key)
                    .addAttribute("content", value);

    }
}
